import {Pipe, PipeTransform} from '@angular/core';
import {EntityRef} from '../entity/entity-ref';
import {Entity} from '../entity/entity';
import {DocService} from '../service/doc.service';
import {StringUtils} from '../utils/string-utils';
import {EntityUtils} from '../utils/entity-utils';

@Pipe({
    name: 'entityJson'
})
export class EntityJsonPipe implements PipeTransform {
    /**
     * 一些类型的默认值
     */
    static TYPE_DEFAULT_VALUES = {
        'java.lang.String': '""',
        'String': '""',
        'java.lang.Long': '0',
        'Long': '0',
        'long': '0',
        'java.lang.Integer': '0',
        'Integer': '0',
        'int': '0',
        'java.lang.Double': '0.0',
        'Double': '0.0',
        'double': '0.0',
        'java.lang.Float': '0.0',
        'Float': '0.0',
        'float': '0.0',
        'java.math.BigDecimal': '0.0',
        'BigDecimal': '0.0',
        'java.lang.Boolean': 'true',
        'Boolean': 'true',
        'boolean': 'true',
        'java.lang.Byte': '""',
        'Byte': '""',
        'byte': '""',
        'java.lang.Character': '""',
        'Character': '""',
        'char': '""',
        'Date': '"2018-08-08 17:29:00"',
    };

    constructor(private docService: DocService) {

    }

    private static renderDefaultBaseTypeDemo(typeName: string): string {
        const defaultValue = EntityJsonPipe.TYPE_DEFAULT_VALUES[typeName.toString()];
        if (defaultValue == null) {
            return '';
        }
        return defaultValue;
    }

    private static renderOneLineComment(comment: string, level: number = 0): string {
        if (comment && !StringUtils.contains(comment, '\n')) {
            return ' #' + comment;
        } else {
            return '';
        }
    }

    private static renderMultiLineComment(comment: string, level: number = 0): string {
        if (comment && StringUtils.contains(comment, '\n')) {
            const tab = StringUtils.repeatT(level + 1);
            comment = comment.replace(/\n/g, '\n' + tab + ' * ');
            return `\n${tab}/*
${tab} * ${comment}
${tab} */\n`;
        }
        return '';
    }

    transform(entryRef: EntityRef, renderComment: boolean = true): string {
        const renderedEntitySet = new Set<Entity>();
        if (renderComment) {
            return `\`\`\`json\n${this.toJsonString(entryRef, 0, renderedEntitySet, renderComment)}\n\`\`\``;
        } else {
            return this.toJsonString(entryRef, 0, renderedEntitySet, renderComment);
        }
    }

    private toJsonString(entryRef: EntityRef, level: number, rendedEntitySet: Set<Entity>, renderComment: boolean): string {
        if (!entryRef) {
            return '';
        }
        const entity: Entity = this.docService.getEntity(entryRef.entityName);
        if (!entity) {
            return '';
        }
        if (entity.collection && entity.collection === true) {
            // 这是列表
            return this.toListJsonString(entryRef, entity, level, rendedEntitySet, renderComment);
        } else if (entity.map && entity.map === true) {
            // 这是Map
            return this.toMapJsonString(entity, level, rendedEntitySet, renderComment);
        }
        return this.toEntityJsonString(entity, level, rendedEntitySet, renderComment);
    }

    private toListJsonString(entryRef: EntityRef, entity: Entity, level: number, rendedEntitySet: Set<Entity>, renderComment: boolean): string {
        const json = [];
        let parameteredEntity: Entity;
        const parameteredEntityRefs: EntityRef[] = entity.parameteredEntityRefs;
        if (parameteredEntityRefs && parameteredEntityRefs.length > 0) {
            const parameterEntityRef: EntityRef = parameteredEntityRefs[0];
            parameteredEntity = this.docService.getEntity(parameterEntityRef.entityName);
        }

        if (EntityUtils.isPrimitive(parameteredEntity) && entryRef.demo) {
            // 基本类型
            json.push(entryRef.demo);
        } else {
            json.push(StringUtils.repeatT(level));
            json.push('[\n');
            console.log(entryRef.name + '=' + '【' + entryRef.entityName + '】' + + entity.enumerate);
            console.log(entryRef);
            console.log(parameteredEntity);
            json.push(this.toEntityJsonString(parameteredEntity, level + 1, rendedEntitySet, renderComment));
            json.push('\n');
            json.push(StringUtils.repeatT(level));
            json.push(']');
        }
        return json.join('');
    }

    private toMapJsonString(entity: Entity, level: number, rendedEntitySet: Set<Entity>, renderComment: boolean): string {
        const json = [];
        json.push(StringUtils.repeatT(level));
        json.push('{ ');
        if (renderComment) {
            json.push('// @');
            json.push(StringUtils.toSimpleTypeName(entity.name));
            if (!StringUtils.isNullOrEmpty(entity.comment)) {
                json.push(EntityJsonPipe.renderOneLineComment(entity.comment, level));
            }
        }
        json.push('\n');

        const ps: EntityRef[] = entity.parameteredEntityRefs;
        if (ps.length === 2) {
            const keyRef: EntityRef = ps[0];
            const valueRef: EntityRef = ps[1];
            json.push(StringUtils.repeatT(level + 1));
            if (keyRef.entityName === 'java.lang.String') {
                json.push('"key"');
            } else {
                json.push(this.renderFieldDemo(keyRef, level + 1, rendedEntitySet, renderComment));
            }
            json.push(': ');
            json.push(this.renderFieldDemo(valueRef, level + 1, rendedEntitySet, renderComment));
        }
        json.push('\n');
        json.push(StringUtils.repeatT(level));
        json.push('}');
        return json.join('');
    }

    private toEntityJsonString(entity: Entity, level: number, rendedEntitySet: Set<Entity>, renderComment: boolean): string {
        if (!entity) {
            return '';
        }
        const json = [];
        if (rendedEntitySet.has(entity)) {
            json.push(StringUtils.repeatT(level));
            json.push('{\n');
            json.push(StringUtils.repeatT(level + 1));
            if (renderComment) {
                json.push('// 循环实体，详见上面 @');
                json.push(entity.name);
            }
            json.push('\n');
            json.push(StringUtils.repeatT(level));
            json.push('}');
            return json.join('');
        }
        if (EntityUtils.isPrimitive(entity)) {
            // 是基础类型
            json.push(StringUtils.repeatT(level));
            json.push(EntityJsonPipe.renderDefaultBaseTypeDemo(entity.name));
            if (renderComment) {
                json.push(' // @');
                json.push(StringUtils.toSimpleTypeName(entity.name));
            }
            return json.join('');
        }

        rendedEntitySet.add(entity);

        json.push(StringUtils.repeatT(level));
        json.push('{ ');
        if (renderComment) {
            json.push('// @');
            json.push(StringUtils.toSimpleTypeName(entity.name));
            const comment = entity.comment;
            if (!StringUtils.isNullOrEmpty(comment)) {
                json.push(EntityJsonPipe.renderOneLineComment(comment, level));
            }
        }
        json.push('\n');

        const fields = entity.fields;
        if (fields && fields.length > 0) {
            // 渲染每个属性
            let i = 0;
            fields.forEach(fieldRef => {
                if (renderComment) {
                    json.push(EntityJsonPipe.renderMultiLineComment(fieldRef.comment, level));
                }
                json.push(StringUtils.repeatT(level + 1));
                json.push('"');
                let propertyName = fieldRef.name;
                const propertyType = StringUtils.toSimpleTypeName(fieldRef.entityName);

                if (/^is[A-Z]/.test(propertyName) && (propertyType === 'Boolean' || propertyType === 'boolean')) {
                    // 如果属性是is开头的boolean类型，则会将is去掉
                    propertyName = propertyName.substr(2, 1).toLocaleLowerCase() + propertyName.substr(3);
                }
                json.push(propertyName);
                json.push('": ');
                json.push(this.renderFieldDemo(fieldRef, level, rendedEntitySet, renderComment));
                if (i < fields.length - 1) {
                    json.push(',');
                }
                if (renderComment) {
                    json.push(' // ');

                    const systemComment = ' !!系统注释：Date类型返回值会根据配置不一定以此格式，建议@demo或使用DateTimeFormat注解指明';
                    if (!fieldRef.demo && propertyType === 'Date' && fieldRef.comment.indexOf(systemComment) === -1) {
                        fieldRef.comment += systemComment;
                    }

                    json.push(propertyType);
                    json.push(EntityJsonPipe.renderOneLineComment(fieldRef.comment, level));
                    if (fieldRef.required && fieldRef.required === true) {
                        json.push(' @NotNull');
                    }
                }
                if (i < fields.length - 1) {
                    json.push('\n');
                }
                ++i;
            });
        }

        json.push('\n');
        json.push(StringUtils.repeatT(level));
        json.push('}');

        return json.join('');
    }

    private renderFieldDemo(fieldRef: EntityRef, level: number, rendedEntitySet: Set<Entity>, renderComment: boolean): string {
        const entityName = fieldRef.entityName;
        const entity = this.docService.getEntity(entityName);
        let demoVal: string;

        if (entity.collection && entity.collection === true) {
            // 如果是列表
            demoVal = this.toListJsonString(fieldRef, entity, level + 1, rendedEntitySet, renderComment);
        } else if (entity.map && entity.map === true) {
            // 如果是Map
            demoVal = this.toMapJsonString(entity, level + 1, rendedEntitySet, renderComment);
        } else if (entity.enumerate) {
            // 如果是枚举
            demoVal = fieldRef.demo;
            demoVal = '"' + demoVal + '"';
        } else if (EntityUtils.isPrimitive(entity)) {
            // 原始数据类型
            demoVal = fieldRef.demo;
            if (demoVal == null) {
                return EntityJsonPipe.renderDefaultBaseTypeDemo(entityName);
            }

            const turnLineIndex = demoVal.indexOf('\n');
            if (turnLineIndex !== -1) {
                demoVal = demoVal.substr(0, turnLineIndex);
            }
            if (entityName.indexOf('java.lang.String') === 0 || entity.enumerate) {
                demoVal = demoVal.replace(/"/g, '\\"');
                demoVal = '"' + demoVal + '"';
            } else if (entityName.indexOf('java.util.Date') === 0 || entityName.indexOf('Date') === 0) {
                if (!/^\d+$/.test(demoVal)) {
                    // 如果不是数值型
                    demoVal = '"' + demoVal + '"';
                }
            }
        } else {
            demoVal = this.toEntityJsonString(entity, level + 1, rendedEntitySet, renderComment);
        }
        // 清除前后空格
        demoVal = demoVal.trim();
        return demoVal;
    }
}
